<?php
/**
 * Created by PhpStorm.
 * User: eValor
 * Date: 2019-03-20
 * Time: 16:57
 */

namespace App\GameService;

use App\Model\GameType;
use App\WebSocket\WsBase;
use App\Utility\BaseEnum;
use App\Common\AuthCommon;
use App\Common\BaseComm;
use App\Utility\RedisTools;
use EasySwoole\Component\Timer;
use App\Enum\SysEnum;
use EasySwoole\EasySwoole\Task\TaskManager;

/**
 * Class Index
 * @package App\WebSocket\Controller
 */
class Index extends WsBase
{
    protected $oneTimer = null;
    protected $bobbyTimer = null;
    /**
     * @return NULL
     */
    public function index() {
        $uniosix = $this->paramsBean->getUniosix();
        if (isset($uniosix) && $uniosix === -1) {
            return "";
        }
        return $this->gamelist(); //转回游戏大厅处理
    }
    
    /**
     * 游戏大厅；验证成功进入游戏列表页面
     * 游戏列表数据
     */
    public function gamelist() {
        $userid = FALSE;    //登录UID
        $loginFd = $this->paramsBean->getFd();
        //获取在线的游戏列表数据；
        try {
            //TODO 登录验证
            $loginUser = $this->LoginPlayUser($userid);
            if (!$loginUser || $userid <= 0 ) { return FALSE; }
            
            $loginUser = AuthCommon::getLoginUser($loginFd);
            $isNation = $loginUser["isNation"] ?? 0;
            $agentId = $loginUser["recUid"] ?? 0; //上级代理AgentId
            $agGameTypes = FALSE;
            // 登录后即进入大厅列表
            $gameModel = new GameType();
            $gameList = $gameModel->getList(30,TRUE);
            //计算当前在线人数据 ；
            if(!$gameList){
                $this-> ResError(BaseEnum::MSG_0XF0,"游戏列表为空");
            }else{
                $roomNums = BaseComm::getRoomOnlines();
                BaseEnum::Logger("房间列表：Onlines=". print_r($roomNums,true), 'user-login');
                
                //TODO 获取当前代理下面的是否有维护中的游戏类型
                $ageGames = $gameModel->getAgentGames($agentId);
                $agGameTypes = !empty($ageGames) ?array_column($ageGames, "gameStatus","gameId") : false ;  //代理下边的游戏类型
                BaseEnum::Logger("查询代理{$agentId}下开放的(Nation = {$isNation})游戏类型：agGames = ".json_encode($agGameTypes), 'user-login');
                foreach ($gameList as $r => $nogame){
                    $onegid = isset($nogame["gameId"]) ? intval($nogame["gameId"]) : 0 ;
                    $gameList[$r]["onlineplayer"] = 0;                  //在线人数更新
                    if(isset($isNation) && intval($isNation) === 0){
                        $newStatus = $agGameTypes[$onegid] ?? 0 ;       //新状态值
                        $gameList[$r]["status"] = intval($newStatus);    //对于普通代理走status状态用上级代理配置值
                    }
                    if (!empty($roomNums) && isset($roomNums[$onegid]) && intval($roomNums[$onegid] > 0) ) {
                        $gameList[$r]["onlineplayer"] = intval($roomNums[$onegid]);
                    }
                }
                $gameList2 = !empty($gameList) ? array_column($gameList, null, "gameId"): false ;
                
                //TODO 进入游戏大厅数据
                BaseComm::doLobbyInOrOut($userid, $loginFd,TRUE);               //TODO 停留在游戏大厅的FD放入内存中
                $this -> startSyncOnlines($loginFd);                            //TODO 异步启动定时一个轮循推送在线数据推送协议
                $this->asycReConnect($userid,$loginFd,$loginUser);              //TODO 断线重连处理；
                $this->ResOK($gameList2, "获取游戏列表成功");
            }
        } catch (\Throwable $e) {
            BaseEnum::Logger("执行异常：".$e->getMessage(), 'user-login');
            return false;
        }
        return TRUE;
    }
    
    
    protected static function getRumTimerObj($rediskey,&$oldTimer = null) {
        //检测是否有定时器在运行，如果没有就启动( 已存在就停掉)
        $timedata = RedisTools::Pool_get($rediskey); //最后的数据
        $timedata = !empty($timedata)  ? json_decode($timedata,true) : FALSE;
        $oldTimer = isset($timedata['mt']) ? intval($timedata['mt']): 0;
        return $timedata;
    }
    
    
    
    /**
     * TODO 轮循推送在线数据推送协议
     */
    protected function startSyncOnlines($loginFd = 0){
        $timeLong = 15000 ;              //TODO 定时器检测；
        $rediskey = SysEnum::ONLINE_Lobby_TIMER_KEY;
        $redisfdskey = SysEnum::ONLINE_Lobby_USERFD_KEY;
        $timedata = RedisTools::Pool_get($rediskey);            //定时器最后的数据
        $timedata = !empty($timedata) ? json_decode($timedata,true) : FALSE;
        $nowTime = BaseEnum::getNowTime(FALSE);
        BaseEnum::Logger("进入大厅定时器检查:" . json_encode(["login--fd"=> $loginFd,"timerobj"=> $timedata]),"user-login");
        $timeStatus = isset($timedata["openStatus"]) ? intval($timedata["openStatus"]) : 0; //定时器状态
        
        //TODO : 定时器一直在运行中(有最新的时间)
        if(!empty($timedata) && $timeStatus === 1 ){   //已经开启：暂停
            
            return FALSE;
        }else if(!empty($timedata) && isset($timedata['mtime']) && $nowTime - $timedata['mtime'] >= 120){
            //TODO ：相隔太久的定时器很久没更新就清理掉
            RedisTools::Pool_del($rediskey);
        }
        
        
        //TODO 用异步来处理在线列家人数
        TaskManager::getInstance()->async(function() use ($rediskey,$redisfdskey,$timeLong,$timeStatus){
            
            //TODO 开启定时器
            Timer::getInstance()->loop($timeLong, function() use($rediskey,$redisfdskey,$timeStatus) {
                try {
                    if($timeStatus <= 0 ){
                        $timedata = RedisTools::Pool_get($rediskey);                    //定时器最后的数据
                        $timedata = !empty($timedata) ? json_decode($timedata,true) : FALSE;
                        $timeStatus = $timedata["openStatus"] ?? 0;                     //定时器回写TimerId
                    }
                    
                    //TODO ：正常情况下是要根据FD的情况来处理[uid=>fd]
                    $allFds = RedisTools::Pool_hGetAll($redisfdskey);
                    BaseEnum::Logger("大厅在线人数据推送:" . json_encode(["Timer--status"=> $timeStatus,"userFD"=> $allFds]),"user-login");
                    
                    //TODO 正常的推送每个位置的在线人数；
                    if(!empty($allFds) && sizeof($allFds) > 0){
                        $onlinePeop = BaseComm::getRoomOnlines();
                        if (!empty($onlinePeop)) {
                            $fjArray = [];
                            foreach ($onlinePeop as $gameid => $onevv) {
                                $fjArray[] = ["gameId"=>$gameid,"onlineplayer" => intval($onevv),"status" => 1 ];
                            }
                            $pushData = ['m'=> BaseEnum::XYM_0X13,"data"=> $fjArray];
                            AuthCommon::pushFrontWeb($allFds,json_encode($pushData));           //推送给WEB端
                        }
                    }else {
                        BaseEnum::Logger("大厅在线人数清零后清理, 定时器正常运行，:" . json_encode(["nowtime"=> BaseEnum::getNowTime(FALSE),"timer--status"=>$timeStatus]),"user-login");
                    }
                    
                    //TODO 每次都更新操作时间
                    $newTimeData = ["openStatus"=> $timeStatus,"mtime"=> BaseEnum::getNowTime(FALSE)];
                    RedisTools::Pool_set($rediskey, json_encode($newTimeData), 600);
                } catch (\Throwable $throwable) {
                    BaseEnum::Logger("大厅定时器执行异常补获:ERROR-" . json_encode(["code"=> $throwable->getCode(),"msg"=> $throwable->getMessage()]),"user-login");
                }
            });
            
            //TODO 第一次保存；协程处理
            $firstdata = ["openStatus"=> 1,"mtime"=> BaseEnum::getNowTime(FALSE)];
            go(function() use($rediskey,$firstdata){
                RedisTools::Pool_set($rediskey, json_encode($firstdata), 600);  
            });
        });
    }
    
    /**
     * ||=========登录后的玩家查看有没有还在游戏中的数据=====玩家游戏中的数据要取出来============||
     * @param int $userid
     * @param int $loginFd
     * @param boolean $other
     */
    protected function asycReConnect($userid,$loginFd,$other=false) {
        
        //TODO  百人牛牛列表跳出 成功后就删掉当前用户在房间列表的操作；
        $allUserFds = BaseComm::distMemoryFjListUser(105,FALSE);
        if (!empty($allUserFds) && isset($allUserFds[$userid]) && $userid > 0) {
            unset($allUserFds[$userid]);
            BaseComm::distMemoryFjListUser(105,TRUE,$allUserFds);   //TODO　当前玩家跳出列表
        }
        
        $this->oneTimer = Timer::getInstance() -> after(2000, function() use($userid,$loginFd) {
            $gamedata = false ;
            //TODO　检测出玩家上次掉线的是在哪个游戏中（非百人牛牛类型）
            if ($userid > 0 && AuthCommon::checkGamesOnline($userid,$loginFd,$gamedata) === TRUE && !empty($gamedata)) {
                
                //游戏是否处理完
                $result = ['m' => BaseEnum::XYM_0X08,"data"=> $gamedata,'msg'=>'游戏数据'];
                $datajson = json_encode($result,JSON_UNESCAPED_UNICODE,JSON_UNESCAPED_SLASHES);
                AuthCommon::pushFrontWeb($loginFd,$datajson);                               //推送给前端异步处理
            }
        });
    }
    
    
    /**
     * TODO 游戏房间列表
     * 获取所有已开放的房间；并返回房间数据
     */
    public function gameDetail() {
        $userId = FALSE;
        $data = $this->paramsBean->getData(true);
        $loginFd = $this->paramsBean->getFd();
        $gameObject = $this -> paramsBean ->getGameObject();
        $gameId = isset($data['gameId']) ? intval($data['gameId']) : 0 ;
        if(!$gameId){
            $this-> ResError(BaseEnum::MSG_0XF2,"游戏类型参数丢失"); 
        }else{
            //获取在线的游戏列表数据；
            $fjField = ['gameId','roomId','roomName','smallPoints','maxInPoints','minPlayer','everyPoints','status','startInPoints'];
            try {
                
                //TODO 登录验证
                $loginUser = $this->LoginPlayUser($userId);
                if (!$loginUser || $userId <= 0 ) { return FALSE; }
                if($this -> pullGameStatus($gameId,$loginUser,FALSE) === FALSE){
                    $this->ResError(BaseEnum::MSG_0XF5,"该游戏暂未开放，请重试", SysEnum::RETURN_MSG_LEVEL_2);           //重新刷新
                    return TRUE;
                }
                
                
                $gameModel = new GameType();
                $gameList = $gameModel->getInfo($gameId,TRUE,$fjField);
                $roomLists = isset($gameList["rooms"]) ? $gameList["rooms"] : FALSE ;
                
                //房间数 ；
                $allNums = (!empty($roomLists)) ? sizeof($roomLists) : 0 ;
                
                if(!$gameList || $allNums <= 0){
                    $this-> ResError(BaseEnum::MSG_0XF5,"暂无该游戏或没有可用房间",SysEnum::RETURN_MSG_LEVEL_2);
                }else{
                    $gameList["roomNums"] = $allNums;
                    //处理不同游戏的数据；
                    if (!empty($gameObject) && method_exists($gameObject, "loopRoomsDetail")) {
                        //拉取房间内的数据
                        $gameObject -> loopRoomsDetail($userId,$loginFd,$roomLists);
                        /* if (!empty($gameList["rooms"]) && isset($details) && !empty($details)) {
                            foreach ($gameList["rooms"] as $grid => $roomv){
                                $roomid2 = isset($roomv['roomId']) ? intval($roomv['roomId']) :0 ;
                                $oneInfo = isset($details[$roomid2]) ? $details[$roomid2] : FALSE;
                                if(!empty($oneInfo)){
                                    $gameList["rooms"][$grid]["onlineStatus"] = isset($oneInfo['onlineStatus']) ? $oneInfo['onlineStatus'] : "";   //游戏房间的动态数据
                                    $gameList["rooms"][$grid]["onlineLeftTime"] = isset($oneInfo['onlineLeftTime']) ? $oneInfo['onlineLeftTime'] : "";
                                    $gameList["rooms"][$grid]["history"] = isset($oneInfo['history']) ? $oneInfo['history'] : [];
                                }
                            }
                        } */
                    }
                    
                    //TODO　除掉大厅列表FD
                    BaseComm::doLobbyInOrOut($userId,FALSE,FALSE); 
                    
                    //TODO 同时把从百人牛牛页面退出游戏页面（清空百人牛牛玩家列表清空）
                    if($allNums > 0 && !empty($roomLists)){
                        foreach ($roomLists as $oneroom){
                            $currentRid = isset($oneroom["roomId"]) ? intval($oneroom["roomId"]): 0;
                            $allPlayers = BaseComm::distMemoryPlayer($currentRid,FALSE,null,TRUE);
                            if(!empty($userId) && $currentRid > 0 && !empty($allPlayers) && array_key_exists($userId, $allPlayers)){
                                unset($allPlayers[$userId]);
                                $allPlayers = !empty($allPlayers) ? $allPlayers : FALSE ;
                                BaseComm::distMemoryPlayer($currentRid,TRUE,$allPlayers,TRUE);  //保存原有的数据
                            }
                        }
                    }
                    //返回WEB数据
                    $this->ResOK($gameList, "获取游戏列表成功");
                }
            } catch (\Throwable $e) {
                BaseEnum::Logger($e->getMessage(), 'user-login');
                return false;
            }
        }
        return TRUE;
    }
    
    
    
    /**
     * 掉线重连操作的数据
     * @return boolean
     */
    public function gameReConnect() {
        $userid = FALSE;
        $loginFd = $this->paramsBean->getFd();
        $gameOObject = $this->paramsBean->getGameObject();
        BaseEnum::Logger('实例化：整体 (getGameObject)---playNewGame >>>' . json_encode($gameOObject), 'user-login');
        //获取在线的游戏列表数据；
        try {
            
            //TODO 登录验证
            $loginUser = $this->LoginPlayUser($userid);
            if (!$loginUser || $userid <= 0 ) { return FALSE; }
            
            //取玩家ID对应最后一次游戏数据； json_encode([$NewgameNo,$gameRId])
            $newGameStr = BaseComm::distLastGameNoRedis($userid);
            $lists = !empty($newGameStr) ? json_decode($newGameStr,TRUE) : FALSE;
            $newGameNo = !empty($lists) ?reset($lists) : FALSE;
            
            //断线重连数据结构组装；
            if (isset($gameOObject) && !empty($gameOObject) && method_exists($gameOObject, 'reConnectPlay') && !empty($newGameNo)) {
                $fullData = $gameOObject->reConnectPlay($userid,$loginFd,$newGameNo);  //重连游戏；FD更新
                if (!empty($fullData)) {
                    $this->ResOK($fullData,"数据恢复");
                    return TRUE;
                }
            }else{
                BaseEnum::Logger("游戏恢复失败： (gameOObject)---reConnectPlay >>> 失败， 暂无数据[gameNo={$newGameNo}]", 'user-login');
            }
        } catch (\Throwable $e) {
            BaseEnum::Logger("游戏恢复异常". $e->getMessage(), 'user-login');
        }
        return FALSE;
    }
    
    
    /**
     * PING 数据包
     */
    public function doPing(){
        $host = "www.baidu.com"; //默认指定一条主域来处理
        $host = $host ?? '127.0.0.1';
        $timelong = BaseEnum::pingCount($host);
        if($timelong<=0){
            $timelong = rand(10,20);
        }
        $this->ResOK(["now"=> BaseEnum::getNowTime(FALSE),"ping" => $timelong ]);
        return TRUE;
    }
}
?>
